const md5 = require("md5");
const mysql = require("mysql");
const redis = require("ioredis");

module.exports = function (config) {
  // 初始化mysql连接池
  let mysql_key = "mysql_".concat(md5(JSON.stringify(config.mysql)));
  if (!global[mysql_key]) {
    global[mysql_key] = mysql
      .createPool(config.mysql)
      .on("acquire", function (connection) {
        console.log("Connection %d acquired", connection.threadId);
      })
      .on("connection", function (connection) {
        console.log("mysql connection");
      })
      .on("enqueue", function () {
        console.log("Waiting for available connection slot");
      })
      .on("release", function (connection) {
        console.log("Connection %d released", connection.threadId);
      });
  }

  let ddd_mysql_pool = global[mysql_key];

  // 初始化redis连接
  let redis_key = "redis_".concat(md5(JSON.stringify(config.redis)));
  if (!global[redis_key])
    global[redis_key] = new redis(config.redis)
      .on("connect", (origin, targets) => {
        console.log(`redis connected`);
      })
      .on("ready", (origin, targets) => {
        console.log(`redis ready`);
      })
      .on("error", (origin, targets) => {
        console.log(`redis error`, origin, targets);
      })
      .on("close", (origin, targets) => {
        console.log(`redis close`, origin, targets);
      })
      .on("reconnecting", (origin, targets) => {
        console.log(`redis reconnecting`, origin, targets);
      })
      .on("end", (origin, targets) => {
        console.log(`redis end`, origin, targets);
      });
  let ddd_redis_client = global[redis_key];

  // 初始化token_cache
  let nodecache_key = "nodecache_".concat(md5(JSON.stringify(config.redis)));
  if (!global[nodecache_key]) {
    const cache = require("node-cache");
    global[nodecache_key] = new cache({
      stdTTL: 30, //标准ttl=30秒。
      checkperiod: 120, //每120秒删除一次过期键值。
    });
  }
  let ddd_token_cache = global[nodecache_key];
  console.log({ mysql_key, redis_key, nodecache_key });

  const ddd = {
    /**
     *
     * 列出所以ddd存储过程名
     *
     * @param {}
     * @returns
     */
    list: function (p) {
      return new Promise((resolve, reject) => {
        let cmd =
          "select substring(ROUTINE_NAME,5) as ROUTINE_NAME FROM information_schema.Routines where ROUTINE_SCHEMA = ? and ROUTINE_NAME like 'ddd_%'";
        ddd_mysql_pool.query(
          cmd,
          [config.mysql.database],
          (err, result, fields) => {
            if (err)
              reject({
                errno: err.errno,
                dddState: err.sqlState,
                sqlMessage: err.sqlMessage,
              });
            else {
              let data = [];
              result.forEach((row) => {
                // console.log(row);
                data.push({ name: row.ROUTINE_NAME });
              });
              resolve(data);
            }
          }
        );
      });
    },

    exec: function ({ sp, token, data }) {
      return this.token_resolve(token).then((jtoken) => {
        return new Promise((resolve, reject) => {
          ddd_mysql_pool.query(
            "select ? into @jdata;call ??(?,@jdata);select @jdata as jdata;",
            [JSON.stringify(data), sp, jtoken],
            (err, result, fields) => {
              if (!err) {
                // console.log({ result });
                try {
                  let res = result.pop();
                  res = JSON.parse(res[0]?.jdata || {});
                  resolve(res);
                } catch (error) {
                  reject(error);
                }
              } else {
                console.log({ error: err });
                reject(err);
              }
            }
          );
        });
      });
    },

    token_resolve: function (token) {
      return new Promise((resolve, reject) => {
        if (!token) resolve("{}");
        else if (typeof token !== "string") resolve(JSON.stringify(token));
        else {
          let tokenKey = `token:${token}`;
          let jtoken = ddd_token_cache.get(tokenKey);
          if (jtoken) resolve(jtoken);
          else {
            this.getRedis(tokenKey)
              .then((jtoken) => {
                if (jtoken) {
                  ddd_token_cache.set(tokenKey, jtoken);
                  resolve(jtoken);
                } else {
                  reject("token not found");
                }
              })
              .catch((err) => {
                reject(err);
              });
          }
        }
      });
    },

    getRedis: function (key) {
      return ddd_redis_client.get(key);
    },

    clearRedis: function (key) {
      return ddd_redis_client.expire(key, 0);
    },

    setRedis: function (key, value, expire) {
      return expire === -1
        ? ddd_redis_client.set(key, value)
        : ddd_redis_client.set(key, value, "EX", expire || 300);
    },

    authcheck: (req, res, next) => {
      if (config.authAllowPath) {
        let isWhiteList = config.authAllowPath.some((item) => {
          // let reg = new RegExp(item);
          return item.test(req.url);
          // return req.url.toLowerCase().includes(item.toLowerCase());
        });
        let token = req.cookies.token;
        if (isWhiteList) {
          next();
        } else if (token) {
          ddd
            .token_resolve(token)
            .then((jtoken) => {
              req.user = JSON.parse(jtoken);
              next();
            })
            .catch((err) => {
              console.log({ err });
              rejectLogin(req, res);
            });
          // next();
        } else rejectLogin(req, res);
      } else next();

      function rejectLogin(req, res) {
        if (req.method === "GET" || req.method === "HEAD")
          res.redirect(
            `${config.loginurl ?? "/login"}?returnUrl=${encodeURIComponent(
              req.originalUrl
            )}`
          );
        else {
          res.status(401).json({
            err_code: 401,
            err_message: "Login has expired, please login again.",
          });
        }
      }
    },
  };

  return {
    ...ddd,
    ...{
      login: require("./routes/login.js")(config, ddd),
      api: require("./routes/api.js")(config, ddd),
    },
  };
};
